package com.wzg.ftpstresstool.service;

import com.wzg.ftpstresstool.config.FileGeneratorProperties;
import com.wzg.ftpstresstool.config.FtpProperties;
import com.wzg.ftpstresstool.model.FileRecord;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

@Slf4j
@Service
public class FileGeneratorService {
    
    @Autowired
    private FileGeneratorProperties fileGeneratorProperties;
    
    @Autowired
    private FtpProperties ftpProperties;
    
    private Random random = new Random();
    private Long currentId = 1L; // 用于生成递增的数字ID
    
    /**
     * 生成文件的主要方法
     */
    public void generateFiles() {
        try {
            // 确保目录存在
            ensureDirectoriesExist();
            
            // 生成文件记录列表
            List<FileRecord> records = new ArrayList<>();
            Long previousId = null;
            
            log.info("开始生成文件，总数: {}", fileGeneratorProperties.getCount());
            
            // 生成指定数量的文件
            for (int i = 0; i < fileGeneratorProperties.getCount(); i++) {
                FileRecord record = createFile(previousId);
                records.add(record);
                previousId = record.getId();
                
                log.info("已生成文件 {}/{}: {}", i+1, fileGeneratorProperties.getCount(), record.getFileName());
            }
            
            // 重命名文件，添加时间、ID等信息
            for (FileRecord record : records) {
                renameFileWithMetadata(record);
            }
            
            // 使用单个FTP连接上传所有文件
            uploadFilesToFtp(records);
            
            // 将记录写入文件，并获取记录文件名
            String recordFileName = writeRecordsToFile(records);
            
            // 上传记录文件到FTP
            uploadRecordFileToFtp(recordFileName);
            
            log.info("文件生成任务完成");
        } catch (Exception e) {
            log.error("生成文件时发生错误", e);
        }
    }
    
    /**
     * 确保必要的目录存在
     */
    private void ensureDirectoriesExist() throws IOException {
        // 创建生成目录
        Path generatePath = Paths.get(fileGeneratorProperties.getDirectory());
        if (!Files.exists(generatePath)) {
            Files.createDirectories(generatePath);
        }
        
        // 创建记录文件目录
        Path recordPath = Paths.get(fileGeneratorProperties.getRecordDirectory());
        if (!Files.exists(recordPath)) {
            Files.createDirectories(recordPath);
        }
    }
    
    /**
     * 创建单个文件
     */
    private FileRecord createFile(Long previousId) throws IOException {
        // 生成文件ID
        Long id = currentId++;
        
        // 构造文件名,并添加一个自增的id
        String fileName = fileGeneratorProperties.getPrefix() + System.currentTimeMillis() + "_" + id + fileGeneratorProperties.getSuffix();
        String filePath = Paths.get(fileGeneratorProperties.getDirectory(), fileName).toString();
        
        // 创建文件
        File file = new File(filePath);
        try (FileOutputStream fos = new FileOutputStream(file)) {
            // 生成随机内容并写入文件
            byte[] content = new byte[(int) fileGeneratorProperties.getSizeInBytes()];
            random.nextBytes(content);
            fos.write(content);
        }
        
        // 创建文件记录
        FileRecord record = new FileRecord();
        record.setId(id);
        record.setPreviousId(previousId);
        record.setFileName(fileName);
        record.setFilePath(filePath);
        record.setSize(fileGeneratorProperties.getSizeInBytes());
        record.setCreateTime(LocalDateTime.now());
        record.setMd5(calculateMD5(file));
        record.setUploaded(false);
        
        return record;
    }
    
    /**
     * 重命名文件，添加元数据信息
     */
    private void renameFileWithMetadata(FileRecord record) throws IOException {
        //获取当前时间的YYYYMMdd_HHmmss_SSS格式字符串
        String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss_SSS"));
        // 构造新的文件名，包含时间、ID、上一个文件ID、生成时间和MD5值
        String newFileName = String.format("%s_%d_%d_%s_%s%s", 
                fileGeneratorProperties.getPrefix().replace("-", ""),
                record.getId(),
                record.getPreviousId() != null ? record.getPreviousId() : 0,
                time,
                record.getMd5(),
                fileGeneratorProperties.getSuffix());
        
        String oldPath = record.getFilePath();
        String newPath = Paths.get(fileGeneratorProperties.getDirectory(), newFileName).toString();
        
        // 重命名文件
        File oldFile = new File(oldPath);
        File newFile = new File(newPath);
        if (oldFile.renameTo(newFile)) {
            record.setFileName(newFileName);
            record.setFilePath(newPath);
        }
    }
    
    /**
     * 上传文件到FTP
     */
    private void uploadFilesToFtp(List<FileRecord> records) {
        FTPClient ftpClient = new FTPClient();
        try {
            // 连接FTP服务器
            ftpClient.connect(ftpProperties.getServer(), ftpProperties.getPort());
            ftpClient.login(ftpProperties.getUsername(), ftpProperties.getPassword());
            ftpClient.enterLocalPassiveMode();
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            
            // 检查并创建目标目录（如果不存在）
            String uploadDirectory = ftpProperties.getUploadDirectory();
            
            // 如果uploadDirectory不为空，则处理目录切换和创建
            if (uploadDirectory != null && !uploadDirectory.trim().isEmpty()) {
                log.info("准备创建FTP目录: {}", uploadDirectory);
                
                // 处理目录路径，确保以/开头但不以/结尾
                if (!uploadDirectory.startsWith("/")) {
                    uploadDirectory = "/" + uploadDirectory;
                }
                if (uploadDirectory.endsWith("/")) {
                    uploadDirectory = uploadDirectory.substring(0, uploadDirectory.length() - 1);
                }
                
                // 递归创建目录
                String[] dirs = uploadDirectory.split("/");
                StringBuilder path = new StringBuilder();
                boolean directoryCreatedOrExists = true;
                
                for (String dir : dirs) {
                    if (dir.isEmpty()) continue;
                    path.append("/").append(dir);
                    // 检查目录是否存在，如果不存在则创建
                    String currentPath = path.toString();
                    log.debug("检查目录是否存在: {}", currentPath);
                    
                    if (!ftpClient.changeWorkingDirectory(currentPath)) {
                        log.debug("目录 {} 不存在，尝试创建", currentPath);
                        if (ftpClient.makeDirectory(currentPath)) {
                            log.info("成功创建目录: {}", currentPath);
                            ftpClient.changeWorkingDirectory(currentPath);
                        } else {
                            int replyCode = ftpClient.getReplyCode();
                            String replyString = ftpClient.getReplyString();
                            log.error("创建目录失败: {} , FTP回复代码: {}, 回复信息: {}", currentPath, replyCode, replyString);
                            directoryCreatedOrExists = false;
                        }
                    } else {
                        log.debug("目录已存在: {}", currentPath);
                    }
                }
                
                if (!directoryCreatedOrExists) {
                    log.warn("目录创建失败，可能会影响文件上传");
                }

                //切换ftp到目录
                ftpClient.changeWorkingDirectory(uploadDirectory);
            } else {
                log.info("ftp.upload-directory未配置，将在FTP根目录下上传文件");
            }
            
            // 上传所有文件
            for (FileRecord record : records) {
                uploadSingleFile(ftpClient, record);
            }
            
        } catch (IOException e) {
            log.error("FTP连接过程中发生错误", e);
            // 如果连接失败，更新所有记录的状态为失败
            for (FileRecord record : records) {
                if (!record.isUploaded()) { // 只更新尚未上传成功的记录
                    record.setUploaded(false);
                }
            }
        } finally {
            try {
                if (ftpClient.isConnected()) {
                    ftpClient.logout();
                    ftpClient.disconnect();
                }
            } catch (IOException ex) {
                log.error("断开FTP连接时发生错误", ex);
            }
        }
    }
    
    /**
     * 上传单个文件到FTP（使用已建立的连接）
     */
    private void uploadSingleFile(FTPClient ftpClient, FileRecord record) {
        try {
            // 上传文件
            File file = new File(record.getFilePath());
            try (InputStream inputStream = new FileInputStream(file)) {
                // 使用相对路径，因为已经在uploadFilesToFtp方法中切换到了目标目录
                String remotePath = record.getFileName();
                boolean done = ftpClient.storeFile(remotePath, inputStream);
                if (done) {
                    record.setUploaded(true);
                    record.setUploadTime(LocalDateTime.now());
                    log.info("文件上传成功: {}", record.getFileName());
                } else {
                    // 获取FTP服务器返回的错误信息
                    int replyCode = ftpClient.getReplyCode();
                    String replyString = ftpClient.getReplyString();
                    log.error("文件上传失败: {} , FTP回复代码: {}, 回复信息: {}", record.getFileName(), replyCode, replyString);
                }
            }
        } catch (IOException e) {
            log.error("FTP上传过程中发生IO错误: " + record.getFileName(), e);
        } catch (Exception e) {
            log.error("FTP上传过程中发生未知错误: " + record.getFileName(), e);
        }
    }
    
    /**
     * 计算文件的MD5值
     */
    private String calculateMD5(File file) throws IOException {
        try (FileInputStream fis = new FileInputStream(file)) {
            return DigestUtils.md5DigestAsHex(fis);
        }
    }
    
    /**
     * 将文件记录写入记录文件
     */
    private String writeRecordsToFile(List<FileRecord> records) throws IOException {
        // 使用当前时间命名记录文件，并添加配置的前缀，时间格式改为yyyyMMdd_HHmmss
        String currentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
        String recordFileName = fileGeneratorProperties.getRecordPrefix() + currentTime + ".txt";
        String recordFilePath = Paths.get(fileGeneratorProperties.getRecordDirectory(), recordFileName).toString();
        
        try (PrintWriter writer = new PrintWriter(new FileWriter(recordFilePath))) {
            writer.println("文件生成记录");
            writer.println("生成时间: " + LocalDateTime.now());
            writer.println("==========================================");
            writer.println("ID\t文件名\t大小(字节)\t创建时间\tMD5\t上传状态\t上传时间");
            
            for (FileRecord record : records) {
                writer.printf("%d\t%s\t%d\t%s\t%s\t%s\t%s%n",
                        record.getId(),
                        record.getFileName(),
                        record.getSize(),
                        record.getCreateTime(),
                        record.getMd5(),
                        record.isUploaded() ? "成功" : "失败",
                        record.getUploadTime() != null ? record.getUploadTime().toString() : "N/A");
            }
        }
        
        log.info("记录文件已保存: {}", recordFilePath);
        return recordFileName;
    }
    
    /**
     * 上传记录文件到FTP
     */
    private void uploadRecordFileToFtp(String recordFileName) {
        FTPClient ftpClient = new FTPClient();
        try {
            // 连接FTP服务器
            ftpClient.connect(ftpProperties.getServer(), ftpProperties.getPort());
            ftpClient.login(ftpProperties.getUsername(), ftpProperties.getPassword());
            ftpClient.enterLocalPassiveMode();
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            
            // 检查并创建目标目录（如果不存在）
            String uploadDirectory = ftpProperties.getUploadDirectory();
            
            // 如果uploadDirectory不为空，则处理目录切换和创建
            if (uploadDirectory != null && !uploadDirectory.trim().isEmpty()) {
                // 处理目录路径，确保以/开头但不以/结尾
                if (!uploadDirectory.startsWith("/")) {
                    uploadDirectory = "/" + uploadDirectory;
                }
                if (uploadDirectory.endsWith("/")) {
                    uploadDirectory = uploadDirectory.substring(0, uploadDirectory.length() - 1);
                }
                
                // 递归创建目录
                String[] dirs = uploadDirectory.split("/");
                StringBuilder path = new StringBuilder();
                for (String dir : dirs) {
                    if (dir.isEmpty()) continue;
                    path.append("/").append(dir);
                    // 检查目录是否存在，如果不存在则创建
                    String currentPath = path.toString();
                    if (!ftpClient.changeWorkingDirectory(currentPath)) {
                        ftpClient.makeDirectory(currentPath);
                        ftpClient.changeWorkingDirectory(currentPath);
                    }
                }
            } else {
                log.info("ftp.upload-directory未配置，将在FTP根目录下上传记录文件");
            }
            
            // 上传记录文件
            String localRecordFilePath = Paths.get(fileGeneratorProperties.getRecordDirectory(), recordFileName).toString();
            File file = new File(localRecordFilePath);
            
            try (InputStream inputStream = new FileInputStream(file)) {
                // 使用相对路径，根据uploadDirectory是否为空决定远程路径
                String remotePath;
                if (uploadDirectory != null && !uploadDirectory.trim().isEmpty()) {
                    remotePath = uploadDirectory + "/" + recordFileName;
                } else {
                    remotePath = recordFileName;
                }
                
                boolean done = ftpClient.storeFile(remotePath, inputStream);
                if (done) {
                    log.info("记录文件上传成功: {}", recordFileName);
                } else {
                    // 获取FTP服务器返回的错误信息
                    int replyCode = ftpClient.getReplyCode();
                    String replyString = ftpClient.getReplyString();
                    log.error("记录文件上传失败: {} , FTP回复代码: {}, 回复信息: {}", recordFileName, replyCode, replyString);
                }
            }
        } catch (IOException e) {
            log.error("FTP上传记录文件过程中发生IO错误: " + recordFileName, e);
        } catch (Exception e) {
            log.error("FTP上传记录文件过程中发生未知错误: " + recordFileName, e);
        } finally {
            try {
                if (ftpClient.isConnected()) {
                    ftpClient.logout();
                    ftpClient.disconnect();
                }
            } catch (IOException ex) {
                log.error("断开FTP连接时发生错误", ex);
            }
        }
    }
}